package goroutinestu

import (
	"context"
	"fmt"
	"time"
)

/***********************************************************************************************
* Go 协程 goroutine
* 是一种轻量线程，它不是操作系统的线程，而是将一个操作系统线程分段使用，通过调度器实现协作式调度。
* 是一种绿色线程，微线程，它与 Coroutine 协程也有区别，能够在发现堵塞后启动新的微线程。
* 通道 channel类似 Unix 的 Pipe，用于协程之间通讯和同步。协程之间虽然解耦，但是它们和 Channel 有着耦合。
*
* 每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少
* 	goroutine：2KB
* 	线程：8MB
* 线程 	goroutine 切换开销方面，goroutine 远比线程小
* 线程	涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP...等寄存器的刷新
* goroutine：只有三个寄存器的值修改 - PC / SP / DX
 */

//2个线程交替打印 A1B2C3
func Sw() {
	s1 := []string{"A", "B", "C", "D"}
	s2 := []string{"1", "2", "3", "4"}

	//创建channel
	ch := make(chan string)

	go func() {
		for _, v := range s1 {
			ch <- v
			//fmt.Println(v)
		}
		//通道无需每次关闭
		//关闭的作用是告诉接收者该通道再无新数据发送
		//只有发送方需要关闭通道
		close(ch)
	}()

	go func() {
		for _, v := range s2 {
			fmt.Print(<-ch)
			fmt.Print(v)
		}
	}()

	time.Sleep(2 * time.Second)
	println()
}

// 当多个协程同时运行时，可通过 select 轮询多个通道
func SelectStu() {
	c1 := make(chan int)
	c2 := make(chan int)
	c3 := make(chan int)

	go func() {
		time.Sleep(3 * time.Second)
		c1 <- 1
	}()

	go func() {
		time.Sleep(2 * time.Second)
		c2 <- 2
	}()

	go func() {
		time.Sleep(time.Second)
		c3 <- 3
	}()

	//如果所有通道都阻塞则等待，如定义了 default 则执行 default
	//如多个通道就绪则随机选择
	select {
	case v := <-c1:
		fmt.Println(v)
	case v := <-c2:
		fmt.Println(v)
	case v := <-c3:
		fmt.Println(v)
		// default:
		// 	fmt.Println("default")
	}

	//time.Sleep(4 * time.Second)
	println("-----")

}

//chan 指定长度
func chanLeng() {
	c := make(chan int, 2)
	c <- 1
	c <- 2
	//c <- 3 第三个程序就阻塞了到这里了
	fmt.Println(<-c)
	fmt.Println(<-c)

}

//停止
func stopc() {
	f := make(chan bool)
	go func() {
		for {
			time.Sleep(time.Second)
			println("循环")
			select {
			case <-f:
				fmt.Println("退出线程")
				return
			}
		}
	}()

	time.Sleep(5 * time.Second)
	f <- true
	close(f)
}

//Context 是设置截止日期、同步信号，传递请求相关值的结构体
//超时停止
func stop2() {

	//Background 通常被用于主函数、初始化以及测试中，作为一个顶层的 context
	//一般我们创建的 context 都是基于 Background
	baseCtx := context.Background()

	//向 context 添加键值对
	ctx := context.WithValue(baseCtx, "key", "value")
	go func(c context.Context) {
		c.Value("key")
		fmt.Println(c.Value("key"))
	}(ctx)

	timeoutCtx, cancel := context.WithTimeout(baseCtx, time.Second)
	defer cancel()

	go func(ctx context.Context) {
		//time.Ticker 以指定的时间间隔重复的向通道 C 发送时间值
		ticker := time.NewTicker(1 * time.Second)
		for _ = range ticker.C {
			select {
			case <-ctx.Done():
				fmt.Println("child process interrupt...")
				return
			default:
				fmt.Println("enter default")
			}
		}
	}(timeoutCtx)

	time.Sleep(3 * time.Second)
	<-timeoutCtx.Done()
	fmt.Println("main process exit!")

}

/*
• 基于 Channel 编写一个简单的单线程生产者消费者模型
• 队列：
队列长度10，队列元素类型为 int
• 生产者：
每1秒往队列中放入一个类型为 int 的元素，队列满时生产者可以阻塞
• 消费者：
每一秒从队列中获取一个元素并打印，队列为空时消费者阻塞
*/
func stu(timeout time.Duration) {

	blockQue := make(chan int, 10)

	//超时退出
	timeoutCtx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	go func() {
		ticker := time.NewTicker(1 * time.Second)
		for _ = range ticker.C {
			fmt.Println("生产", 1)
			blockQue <- 1
		}

	}()

	go func() {
		ticker := time.NewTicker(1 * time.Second)
		for _ = range ticker.C {
			fmt.Println("消费", <-blockQue)
		}
	}()

	fmt.Println("时间到 main process exit!", <-timeoutCtx.Done())
	close(blockQue)

}
